// 求等差数列前100项的和： 1 + 2 + 3 + 4 + ... + 100 (虽然有公式，但是面对更复杂的运算时，不是数学专业，不一定能快速推出公式)
// 实现一个函数 sum(n) 完成上述要求

// 抛开循环的思想 利用递归的思想
// 前 n 项和： sum(n) = 1 + 2 + 3 + ... + n-1 + n
// 如果想知道 前n项 的和 可以将 sum(n) 拆解为： sum(n) = sum(n-1) + n

// 有了上述通项公式 就可以 依次往前 去寻找 n 前面所有项的和 直到找到某个已知的前 n-1 项的和，就能 返回去 依次 加上 n, 就能得到最终结果；

function sum(n) {
    // 结束条件
    if (n === 1) return 1

    // 递归过程
    return sum(n - 1) + n
}
console.log(sum(100)) // 5050

// 实现一个函数 求 1^5 + 2^5 + ... + n^5

function sum2(n) {
    if (n === 1) return 1

    return sum2(n - 1) + Math.pow(n, 5)
}
console.log(sum2(10)) // 220825

// 验证一下
let n = new Array(10).fill(0).map((i, j) => (j + 1) ** 5).reduce((t, i) => t + i)
console.log(n) // 220825 = sum2(10)

// 设计一函数 求 n 的阶乘
function factorial(n) {
    if (n === 1) return 1

    return factorial(n - 1) * n
}

fibonacci[0] = 1

console.log(factorial(5)) // 120

// 斐波那契数列的第 N 项 1 1 2 3 5 8 13 ...

function fibonacci(n) {
    if (n < 3) return 1
    return fibonacci(n - 1) + fibonacci(n - 2)
}

console.log(fibonacci(12)) // 144
// 计算数目很大的时候 运算效率就非常低
// 修改一下 改成带有记忆功能的函数

// 用于保存记录
let result = {}

function fibonacciX(n) {
    // 计算过的就直接查表
    if (result[n]) return result[n]

    if (n < 3) {
        result[n] = 1
        return result[n]
    }
    // 没有就计算保存
    result[n] = fibonacciX(n - 1) + fibonacciX(n - 2)
    return result[n]
}
console.log(fibonacciX(40)) // 102334155  几乎秒算 非常快

// 也可以利用函数自身属性 函数本质上也是个对象 这样计算过一次之后，计算过的所有结果都永久保留在函数本身上了
function fibonacciPlus(n) {

    if (fibonacciPlus[n]) return fibonacciPlus[n]

    if (n < 3) {
        fibonacciPlus[n] = 1
        return fibonacciPlus[n]
    }

    fibonacciPlus[n] = arguments.callee(n - 1) + arguments.callee(n - 2)
    return fibonacciPlus[n]
}

console.log(fibonacciPlus(400)) // 1.760236806450138e+83 运算效率依然很高

// 小明在楼梯第0阶，现在小明要上到第 n 阶，共有两种上楼方式：
// 一次跨 2 阶 || 一次跨 3 阶
// 设计一个函数 计算小明上到 第n阶的走法数：
// 提示： 理解清楚设计函数的意义 将函数转化成简单问题 将结束条件判断出来...

/* 
    分析：
    a1 = 0   
    a2 = 1      2*1
    a3 = 1      3*1
    a4 = 1      2*2
    a5 = 2      3+2 | 2+3
    a6 = 2      3*2 | 2*3
    a7 = 3      3+2*2 | 2*2+3 | 2+3+2 
    a8 = 4      2*4 | 3*2+2 | 2+3*2 | 3+2+3
    a9 = 5      3*3 | 2223 | 2232 | 2322 | 3222
    ...         ...
    an = an-3 + an-2 递归公式

    结束条件：
    (n - 3) >= 1 ==> n >= 4

    所以 当 n <= 4，就返回 1；n <= 1 返回 0；
*/

function stairway(n) {

    if (stairway[n]) return stairway[n]

    // 结束条件
    if (n <= 1) {
        stairway[n] = 0
        return stairway[n]
    }

    if (n <= 4) {
        stairway[n] = 1
        return stairway[n]
    }

    // 递归式
    stairway[n] = stairway(n - 3) + stairway(n - 2)

    return stairway[n]
}

console.log(stairway(50)) // 525456

// 纯函数：
const 平方 = x => x ** 2
console.log(平方(2)) // 4

// 偏函数
let r = function (x) {
    return function (y) {
        return x + y
    }
}

setTimeout(() => {
    r = r('1')
}, Math.random() * 1000 + 500)

setTimeout(() => {
    r = r('2')
}, Math.random() * 1000 + 500)

setTimeout(() => {
    console.log(r) // 12 || 21 不一定谁先谁后
}, 2000)

let ss = x => y => z => x + y + z
console.log(ss(1)(2)(3)) // 6

// 不定参数的偏函数
const lalala = (...x) => (...y) => (...z) => [...x, ...y, ...z].reduce((t, c) => t + c)
let rr = lalala(1, 2, 3)
rr = rr(4, 5, 6)
console.log(rr(7, 8, 9)) // 45